;;; init-utils.el --- Utils -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:

(defun ctrlwei/list-dirs (dir &optional recursively recursive-symlinks)
  "Return list of subdirectories under DIR(include hide and symlinks dirctorys).
If RECURSIVELY is non-nil, will list all subdirectories.
If RECURSIVE-SYMLINKS is non-nil, will list all subdirectories under symlinks."
  (let ((result (list dir)))
    (dolist (file (file-name-all-completions "" dir))
      (when (and (directory-name-p file) (not (member file '("./" "../"))))
        (nconc result (list (expand-file-name file dir)))
        (when recursively
          (let* ((dir-name (substring file 0 (1- (length file))))
                 (dir-path (expand-file-name dir-name dir)))
            (if (file-symlink-p dir-path)
                (if recursive-symlinks
                    (nconc result (ctrlwei/list-dirs dir-path t t)))
              (nconc result (ctrlwei/list-dirs dir-path t)))))))
    (cdr result)))

(defun ctrlwei/quit-window (&optional window)
  "Quit the selected window and kill its buffer.
This behaves similar to `quit-window', but when the window
was originally created to display a popup buffer and the
current buffer is the last remaining popup buffer that
was ever disabled in the selected window, then delete
that window."
  (interactive)
  (let ((real-window (if window window (selected-window))))
    (if (or (eq 1 (length (window-list (window-frame window))))
            ;; (one-window-p)
            (--first (let ((buffer (car it)))
                       (and (not (eq buffer (window-buffer window)))
                            (buffer-live-p buffer)))
                     (window-prev-buffers window)))
        (quit-window t window)
      (progn
        (quit-window t window)
        (when (window-live-p real-window)
          (delete-window real-window))))))

(defun sudo-find-file (file)
  "Open FILE as root."
  (interactive "FOpen file as root: ")
  (when (file-writable-p file)
    (user-error "File is user writeable, aborting sudo"))
  (find-file (if (file-remote-p file)
                 (concat "/" (file-remote-p file 'method) ":"
                         (file-remote-p file 'user) "@" (file-remote-p file 'host)
                         "|sudo:root@"
                         (file-remote-p file 'host) ":" (file-remote-p file 'localname))
               (concat "/sudo:root@localhost:" file))))

(defun browse-current-file ()
  "Open the current file as a URL using `browse-url'."
  (interactive)
  (let ((file-name (buffer-file-name)))
    (if (and (fboundp 'tramp-tramp-file-p)
             (tramp-tramp-file-p file-name))
        (error "Cannot open tramp file")
      (browse-url (concat "file://" file-name)))))

(defun copy-filename ()
  "Copy the current buffer file name to the clipboard."
  (interactive)
  (let ((filename (if (equal major-mode 'dired-mode)
                      default-directory
                    (buffer-file-name))))
    (unless filename
      (setq filename (buffer-name)))
    (kill-new filename)
    (message "Copied buffer file name \"%s\" to the clipboard." filename)))

(defun delete-this-file ()
  "Delete the current file, and kill the buffer."
  (interactive)
  (unless (buffer-file-name)
    (error "No file is currently being edited"))
  (when (yes-or-no-p (format "Really delete '%s'?"
                             (file-name-nondirectory buffer-file-name)))
    (delete-file (buffer-file-name))
    (kill-current-buffer)))

(defmacro measure-time (&rest body)
  "Measure and return the running time of the code block."
  (declare (indent defun))
  (let ((start (make-symbol "start")))
    `(let ((,start (float-time)))
       ,@body
       (- (float-time) ,start))))

(defun ap/garbage-collect ()
  "Run `garbage-collect' and print stats about memory usage."
  (interactive)
  (message (cl-loop for (type size used free) in (garbage-collect)
                    for used = (* used size)
                    for free = (* (or free 0) size)
                    for total = (file-size-human-readable (+ used free))
                    for used = (file-size-human-readable used)
                    for free = (file-size-human-readable free)
                    concat (format "%s: %s + %s = %s\n" type used free total))))

(defun get-buffers-matching-mode (mode)
  "Returns a list of buffers where their major-mode is equal to MODE"
  (let ((buffer-mode-matches '()))
    (dolist (buf (buffer-list))
      (with-current-buffer buf
        (when (eq mode major-mode)
          (push buf buffer-mode-matches))))
    buffer-mode-matches))

(defmacro define-key-with-arg (map key func &rest args)
  `(define-key ,map ,key (lambda () (interactive) (,func ,@args))))

(defmacro global-set-key-with-arg (key func &rest args)
  `(global-set-key ,key (lambda () (interactive) (,func ,@args))))

(defun xah-replace-invisible-char ()
  "Query replace some invisible Unicode chars.
The chars replaced are:
 ZERO WIDTH NO-BREAK SPACE (65279, #xfeff)
 ZERO WIDTH SPACE (codepoint 8203, #x200b)
 RIGHT-TO-LEFT MARK (8207, #x200f)
 RIGHT-TO-LEFT OVERRIDE (8238, #x202e)
 LEFT-TO-RIGHT MARK ‎(8206, #x200e)
 OBJECT REPLACEMENT CHARACTER (65532, #xfffc)

Search begins at buffer beginning. (respects `narrow-to-region')

URL `http://xahlee.info/emacs/emacs/elisp_unicode_replace_invisible_chars.html'
Version: 2018-09-07 2022-09-13"
  (interactive)
  (let ((case-replace nil)
        (case-fold-search nil)
        ($p0 (point)))
    (goto-char (point-min))
    (while (re-search-forward "\ufeff\\|\u200b\\|\u200f\\|\u202e\\|\u200e\\|\ufffc" nil t)
      (replace-match ""))
    (goto-char $p0)))

(defun generate-tmp-file-name (&optional type)
  "Insert a temporary file name and save it in kill ring."
  (interactive)
  (setq tmpfile (concat "/tmp/"
                        (with-temp-buffer
                          (dotimes (_ 12)
                            (insert
                             (let ((x (random 36)))
                               (if (< x 10) (+ x ?0) (+ x (- ?a 10))))))
                          (buffer-string))
                        type))
  (insert tmpfile)
  (kill-new tmpfile))

(provide 'init-utils)
;;; init-utils.el ends here
